package com.ljp.chat.support.enhancement;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ljp.chat.entity.Result;
import com.ljp.chat.entity.enums.ResultEnum;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

public class AbstractRestSupport
        <P extends Serializable, E, S extends IService<E>> implements RestCrudSupport<E, P> {

    @Autowired
    protected S service;

    private static final String GREAT_THAN = "ge";
    private static final String LESS_THAN = "le";
    private static final String BETWEEN = "bt";
    private static final String DEFAULT_TIME_KEY = "createTime";

    private final KeyConverter converter = new KeyConverter();

    @Override
    public Result<Object> getResultByPagination(Page<E> page, E query, TimestampFilter filter) {
        if (null == page) {
            page = new Page<>();
        }
        List<OrderItem> orders = new ArrayList<>();
        orders.add(OrderItem.asc("id"));
        page.setOrders(orders);
        try {
            IPage<E> records;
            if (null != query) {
                records = service
                        .page(page, getQueryChainWrapper(query, filter));
            } else {
                records = service
                        .page(page);
            }
            if (null == records || records.getRecords().size() == 0) {
                return new Result<>().Result(ResultEnum.SUCCESS);
            }
            return new Result<>().SUCCESS(records);
        } catch (Exception e) {
            return new Result<>().FILED(e.getMessage());
        }
    }

    @Override
    public Result<Object> deleteById(P primaryKey) {
        try {
            boolean isSuccess = service.removeById(primaryKey);
            if (isSuccess) {
                return new Result<>().Result(ResultEnum.SUCCESS);
            } else {
                return new Result<>().FILED("failed to delete data, please check if this primary key exists");
            }
        } catch (Exception e) {
            return new Result<>().FILED(e.getMessage());
        }
    }

    @Override
    public Result<Object> getById(P primaryKey) {
        try {
            E record = service.getById(primaryKey);
            if (null != record) {
                return new Result<>().SUCCESS(record);
            } else {
                return new Result<>().SUCCESS(null);
            }
        } catch (Exception e) {
            return new Result<>().FILED(e.getMessage());
        }
    }

    @Override
    public Result<Object> updateById(E entity) {
        try {
            boolean isSuccess = service.updateById(entity);
            if (isSuccess) {
                return new Result<>().SUCCESS(entity);
            } else {
                return new Result<>().FILED("failed to update data, please check if this primary key exists");
            }
        } catch (Exception e) {
            return new Result<>().FILED(e.getMessage());
        }
    }

    @Override
    public Result<Object> save(E entity) {
        try {
            boolean isSuccess = service.save(entity);
            if (isSuccess) {
                return new Result<>().SUCCESS(entity);
            } else {
                return new Result<>().FILED("an error occurred while processing the data save");
            }
        } catch (Exception e) {
            return new Result<>().FILED(e.getMessage());
        }
    }

    @Override
    public Result<Object> saveOrUpdate(E entity) {
        try {
            boolean isSuccess = service.saveOrUpdate(entity);
            if (isSuccess) {
                return new Result<>().SUCCESS(entity);
            } else {
                return new Result<>().FILED("an error occurred while processing the data");
            }
        } catch (Exception e) {
            return new Result<>().FILED(e.getMessage());
        }
    }

    private QueryWrapper<E> getQueryChainWrapper(E entity, TimestampFilter filter)
            throws IllegalAccessException {
        QueryWrapper<E> queryWrapper = new QueryWrapper<>();
        // 处理实体对象
        Field[] fields = EntityWeapons.getDeclaredFields(entity.getClass());
        for (Field field : fields) {
            field.setAccessible(true);
            Object val = field.get(entity);
            if (null != val) {
                if (String.class.isAssignableFrom(val.getClass())) {
                    if ("null".equals(val)) {
                        continue;
                    }
                }
                TableField tableField = field.getDeclaredAnnotation(TableField.class);
                if (null != tableField) {
                    queryWrapper.eq(tableField.value(), val);
                } else {
                    queryWrapper.eq(converter.convertLine(field.getName()), val);
                }
            }
            field.setAccessible(false);
        }
        if (null != filter) {
            // 指定了开始结束时间
            Integer start = filter.getStart();
            Integer end = filter.getEnd();
            if (null != start || null != end) {
                String ops;
                String key;
                if (null == filter.getOps()) {
                    ops = BETWEEN;
                } else {
                    ops = filter.getOps();
                }

                if (null == filter.getKey()) {
                    key = DEFAULT_TIME_KEY;
                } else {
                    key = filter.getKey();
                }

                key = converter.convertLine(key);

                switch (ops) {
                    case GREAT_THAN:
                        if (null != start) {
                            queryWrapper.ge(key, DateWeapons.timestamp2Date(start.longValue()));
                        }
                        break;
                    case LESS_THAN:
                        if (null != end) {
                            queryWrapper.le(key, DateWeapons.timestamp2Date(end.longValue()));
                        }
                        break;
                    case BETWEEN:
                        if (null != start && null != end) {
                            queryWrapper.between(key, DateWeapons.timestamp2Date(start.longValue()),
                                    DateWeapons.timestamp2Date(end.longValue()));
                        }
                        break;
                    default:
                        break;
                }
            }
        }
        return queryWrapper;
    }
}
